/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package pgentity;

import com.google.gson.Gson;
import db.DBContext;
import db.PGKeys;
import db.RedisKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.pool2.impl.GenericObjectPool;
import share.PGHelper;
import share.StringBuilderPoolFactory;

/**
 *
 * @author Salm
 */
public class Mailbox3 {
    private static final long MAX_MAILS = 100;
    private static final long REM_MAILS = 20;
    
    private static final GenericObjectPool<StringBuilder> SB_MAIL_RENDERER;
    private static final Mailbox3 EMPTY_MAILBOX;
//    private static final LoadingCache<String, Mailbox3> mailCache;
    
    static
    {
        SB_MAIL_RENDERER = StringBuilderPoolFactory.makePool();
//        mailCache = CacheBuilder.newBuilder()
//                .expireAfterWrite(10, TimeUnit.MINUTES)
//                .maximumSize(100)
//                .build(new Mailbox3.Mailbox3Loader());
        
        EMPTY_MAILBOX = new Mailbox3("", Collections.EMPTY_LIST, 0);
    }
    
    private final String uid;
    private final List<Mail3> mails;
    private int unreadMail;
    
    public Mailbox3(String uid, List<Mail3> mails, int nUnread)
    {
        this.uid = uid;
        this.mails = mails;
        this.unreadMail = nUnread;
    }

    private static RedisKey dataKey(String uid)
    {
        return User.redisKey(uid).getChild(PGKeys.FD_DATA_MAILS);
    }

    private static RedisKey unreadKey(String uid)
    {
        return User.redisKey(uid).getChild(PGKeys.FD_UNREAD_MAILS);
    }
    
    public static Mailbox3 getMails(String uid)
    {
        try
        {
            return load(uid);
        }
        catch (Exception ex)
        {
            return EMPTY_MAILBOX;
        }
    }
    
    public static int getUnreadMails(String uid)
    {
        return PGHelper.toInt(DBContext.Redis2().get(Mailbox3.unreadKey(uid)));
    }
    
    public static void destroy(String uid)
    {
        DBContext.Redis2().del(Mailbox3.dataKey(uid));
        DBContext.Redis2().del(Mailbox3.unreadKey(uid));
    }
    
    public static void tryRotate(final String uid)
    {
        try
        {
            long nMails = DBContext.Redis2().llen(dataKey(uid));
            if (nMails > MAX_MAILS)
            {
                DBContext.Redis2().ltrim(Mailbox3.dataKey(uid),
                        0, MAX_MAILS - REM_MAILS);
            }
        }
        catch (Exception ex)
        {
            // ignore that
        }
    }
    
    public static void sendMail(String uid, Mail3 mail)
    {
        try
        {
            DBContext.Redis2().lpush(dataKey(uid),
                    PGHelper.getJSONParser().toJson(mail));
            DBContext.Redis2().incr(unreadKey(uid));
            DBContext.Redis2().expire(dataKey(uid), 604800);
            DBContext.Redis2().expire(unreadKey(uid), 604800);

            tryRotate(uid);
        }
        catch (Exception e)
        {
            // ignore e
        }
    }
    
    public Map<String, Object> buildAMF(int off, int len)
    {
        StringBuilder sb = null;
        try
        {
            sb = SB_MAIL_RENDERER.borrowObject();
            
            off = validOffset(off);
            len = validLen(off, len);
            Map<String, Object> data = new LinkedHashMap(len);

            for (int i = 0; i < len; ++i)
            {
                sb.setLength(0);
                sb.ensureCapacity(0x400);
                int index = off - i - 1;
                data.put(String.valueOf(index), mails.get(index).buildAMF(sb));
            }
            
            return data;
        }
        catch (Exception ex)
        {
            return Collections.EMPTY_MAP;
        }
        finally
        {
            if (sb != null)
            {
                SB_MAIL_RENDERER.returnObject(sb);
            }
        }
    }
    
    public int getUnreadMail()
    {
        return this.unreadMail;
    }
    
    public void resetUnreadMail()
    {
        if (this.unreadMail > 0)
        {
            DBContext.Redis2().decrBy(unreadKey(uid), unreadMail);
        }
        
        this.unreadMail = 0;
    }
    
    private int validOffset(int off)
    {
        if (off < 0)
        {
            return mails.size() + off + 1;
        }
        
        return off;
    }
    
    private int validLen(int off, int len)
    {
        return Math.min(len, off);
    }
    
    private static Mailbox3 load(String uid) {
        List<String> mailsJSON = DBContext
                .Redis2().lrange(dataKey(uid), 0, MAX_MAILS);
        List<Mail3> mails = parseMails(mailsJSON);

        int nUnread = PGHelper.toInt(DBContext.Redis2()
            .get(unreadKey(uid)));

        return new Mailbox3(uid, mails, nUnread);
    }

    private static List<Mail3> parseMails(List<String> json)
    {
        Gson jsonParser = PGHelper.getJSONParser();

        List<Mail3> mails = new ArrayList(json.size());

        for (String mailJS : json) {
            Mail3 mail;
            try
            {
                mail = jsonParser.fromJson(mailJS, Mail3.class);
                mails.add(mail);
            }
            catch (Exception ex)
            {
                // ignore that mail
            }
        }

        return mails;
    }
}